//===--- GenClangType.cpp - Swift IR Generation For Types -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements generation of Clang AST types from Swift AST types
//  for types that are representable in Objective-C interfaces.
//  AST/ClangTypeConverter.cpp duplicates a bunch of code from here, so make
//  sure to keep the two in sync.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/StringSwitch.h"
#include "polarphp/ast/AstContext.h"
#include "polarphp/ast/CanTypeVisitor.h"
#include "polarphp/ast/Decl.h"
#include "polarphp/ast/ExistentialLayout.h"
#include "polarphp/ast/NameLookup.h"
#include "polarphp/pil/lang/PILType.h"
#include "polarphp/clangimporter/ClangImporter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/TargetInfo.h"
#include "polarphp/irgen/internal/IRGenModule.h"

using namespace polar;
using namespace irgen;

/// Global information about importing clang types.
class polar::irgen::ClangTypeConverter {
   llvm::DenseMap<CanType, clang::CanQualType> Cache;

   ClangTypeConverter(const ClangTypeConverter &) = delete;
   ClangTypeConverter &operator=(const ClangTypeConverter &) = delete;

public:
   ClangTypeConverter() = default;
   clang::CanQualType convert(IRGenModule &IGM, CanType type);
   clang::CanQualType reverseBuiltinTypeMapping(IRGenModule &IGM,
                                                CanStructType type);
};

static CanType getNamedPolarphpType(ModuleDecl *stdlib, StringRef name) {
   auto &ctx = stdlib->getAstContext();
   SmallVector<ValueDecl*, 1> results;
   stdlib->lookupValue(ctx.getIdentifier(name), NLKind::QualifiedLookup,
                       results);

   // If we have one single type decl, and that decl has been
   // type-checked, return its declared type.
   //
   // ...non-type-checked types should only ever show up here because
   // of test cases using -enable-source-import, but unfortunately
   // that's a real thing.
   if (results.size() == 1) {
      if (auto typeDecl = dyn_cast<TypeDecl>(results[0]))
         return typeDecl->getDeclaredInterfaceType()->getCanonicalType();
   }
   return CanType();
}

static clang::CanQualType
getClangBuiltinTypeFromKind(const clang::ASTContext &context,
                            clang::BuiltinType::Kind kind) {
   switch (kind) {
#define BUILTIN_TYPE(Id, SingletonId)                                          \
  case clang::BuiltinType::Id:                                                 \
    return context.SingletonId;
#include "clang/AST/BuiltinTypes.def"
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
  case clang::BuiltinType::Id:                                                 \
    return context.SingletonId;
#include "clang/Basic/OpenCLImageTypes.def"
#define EXT_OPAQUE_TYPE(ExtType, Id, Ext)                                      \
  case clang::BuiltinType::Id:                                                 \
    return context.Id##Ty;
#include "clang/Basic/OpenCLExtensionTypes.def"
#define SVE_TYPE(Name, Id, SingletonId)                                        \
  case clang::BuiltinType::Id:                                                 \
    return context.SingletonId;
#include "clang/Basic/AArch64SVEACLETypes.def"
   }

   llvm_unreachable("Not a valid BuiltinType.");
}

static clang::CanQualType getClangSelectorType(
   const clang::ASTContext &clangCtx) {
   return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy);
}

static clang::CanQualType getClangMetatypeType(
   const clang::ASTContext &clangCtx) {
   clang::QualType clangType =
      clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, nullptr, 0);
   clangType = clangCtx.getObjCObjectPointerType(clangType);
   return clangCtx.getCanonicalType(clangType);
}

static clang::CanQualType getClangIdType(
   const clang::ASTContext &clangCtx) {
   clang::QualType clangType =
      clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, nullptr, 0);
   clangType = clangCtx.getObjCObjectPointerType(clangType);
   return clangCtx.getCanonicalType(clangType);
}

static clang::CanQualType getClangDecayedVaListType(
   const clang::ASTContext &clangCtx) {
   clang::QualType clangType =
      clangCtx.getCanonicalType(clangCtx.getBuiltinVaListType());
   if (clangType->isConstantArrayType())
      clangType = clangCtx.getDecayedType(clangType);
   return clangCtx.getCanonicalType(clangType);
}

namespace {
/// Given a Swift type, attempt to return an appropriate Clang
/// CanQualType for the purpose of generating correct code for the
/// ABI.
class GenClangType : public CanTypeVisitor<GenClangType, clang::CanQualType> {
   IRGenModule &IGM;
   ClangTypeConverter &Converter;

public:
   GenClangType(IRGenModule &IGM, ClangTypeConverter &converter)
      : IGM(IGM), Converter(converter) {}

   const clang::ASTContext &getClangAstContext() const {
      return IGM.getClangAstContext();
   }

   /// Return the Clang struct type which was imported and resulted in
   /// this Swift struct type. We do not currently handle generating a
   /// new Clang struct type for Swift struct types that are created
   /// independently of importing a Clang module.
   clang::CanQualType visitStructType(CanStructType type);
   clang::CanQualType visitTupleType(CanTupleType type);
   clang::CanQualType visitMetatypeType(CanMetatypeType type);
   clang::CanQualType visitExistentialMetatypeType(CanExistentialMetatypeType type);
   clang::CanQualType visitInterfaceType(CanInterfaceType type);
   clang::CanQualType visitClassType(CanClassType type);
   clang::CanQualType visitBoundGenericClassType(CanBoundGenericClassType type);
   clang::CanQualType visitBoundGenericType(CanBoundGenericType type);
   clang::CanQualType visitEnumType(CanEnumType type);
   clang::CanQualType visitFunctionType(CanFunctionType type);
   clang::CanQualType visitInterfaceCompositionType(
      CanInterfaceCompositionType type);
   clang::CanQualType visitBuiltinRawPointerType(CanBuiltinRawPointerType type);
   clang::CanQualType visitBuiltinIntegerType(CanBuiltinIntegerType type);
   clang::CanQualType visitBuiltinFloatType(CanBuiltinFloatType type);
   clang::CanQualType visitArchetypeType(CanArchetypeType type);
   clang::CanQualType visitPILFunctionType(CanPILFunctionType type);
   clang::CanQualType visitGenericTypeParamType(CanGenericTypeParamType type);
   clang::CanQualType visitDynamicSelfType(CanDynamicSelfType type);

   clang::CanQualType visitPILBlockStorageType(CanPILBlockStorageType type);

   clang::CanQualType visitType(CanType type);

   clang::CanQualType getCanonicalType(clang::QualType type) {
      return getClangAstContext().getCanonicalType(type);
   }

   clang::CanQualType convertMemberType(NominalTypeDecl *DC,
                                        StringRef memberName);
};
} // end anonymous namespace

clang::CanQualType
GenClangType::convertMemberType(NominalTypeDecl *DC, StringRef memberName) {
   auto memberTypeDecl = cast<TypeDecl>(
      DC->lookupDirect(IGM.Context.getIdentifier(memberName))[0]);
   auto memberType = memberTypeDecl->getDeclaredInterfaceType()
      ->getCanonicalType();
   return Converter.convert(IGM, memberType);
}

static clang::CanQualType getClangVectorType(const clang::ASTContext &ctx,
                                             clang::BuiltinType::Kind eltKind,
                                             clang::VectorType::VectorKind vecKind,
                                             StringRef numEltsString) {
   unsigned numElts;
   bool failedParse = numEltsString.getAsInteger(10, numElts);
   assert(!failedParse && "vector type name didn't end in count?");
   (void) failedParse;

   auto eltTy = getClangBuiltinTypeFromKind(ctx, eltKind);
   auto vecTy = ctx.getVectorType(eltTy, numElts, vecKind);
   return ctx.getCanonicalType(vecTy);
}

clang::CanQualType GenClangType::visitStructType(CanStructType type) {
   auto &ctx = IGM.getClangAstContext();

   auto polarphpDecl = type->getDecl();
   StringRef name = polarphpDecl->getName().str();

   // We assume that the importer translates all of the following types
   // directly to structs in the standard library.

   // We want to recognize most of these types by name.
#define CHECK_NAMED_TYPE(NAME, CLANG_TYPE) do {                         \
    if (name == (NAME)) return CLANG_TYPE;                              \
  } while (false)

   CHECK_NAMED_TYPE("CGFloat", convertMemberType(polarphpDecl, "NativeType"));
   CHECK_NAMED_TYPE("OpaquePointer", ctx.VoidPtrTy);
   CHECK_NAMED_TYPE("CVaListPointer", getClangDecayedVaListType(ctx));
   CHECK_NAMED_TYPE("DarwinBoolean", ctx.UnsignedCharTy);
   // @todo
//   CHECK_NAMED_TYPE(polarphpDecl->getASTContext().getSwiftName(
//      KnownFoundationEntity::NSZone),
//                    ctx.VoidPtrTy);
   CHECK_NAMED_TYPE("WindowsBool", ctx.IntTy);
   CHECK_NAMED_TYPE("ObjCBool", ctx.ObjCBuiltinBoolTy);
   CHECK_NAMED_TYPE("Selector", getClangSelectorType(ctx));
   CHECK_NAMED_TYPE("UnsafeRawPointer", ctx.VoidPtrTy);
   CHECK_NAMED_TYPE("UnsafeMutableRawPointer", ctx.VoidPtrTy);
#undef CHECK_NAMED_TYPE

   // Map vector types to the corresponding C vectors.
#define MAP_SIMD_TYPE(TYPE_NAME, _, BUILTIN_KIND)                      \
  if (name.startswith(#TYPE_NAME)) {                                   \
    return getClangVectorType(ctx, clang::BuiltinType::BUILTIN_KIND,   \
                              clang::VectorType::GenericVector,        \
                              name.drop_front(sizeof(#TYPE_NAME)-1));  \
  }
#include "polarphp/clangimporter/SIMDMappedTypesDef.h"

   // Everything else we see here ought to be a translation of a builtin.
   return Converter.reverseBuiltinTypeMapping(IGM, type);
}

static clang::CanQualType getClangBuiltinTypeFromTypedef(
   clang::Sema &sema, StringRef typedefName) {
   auto &context = sema.getASTContext();

   auto identifier = &context.Idents.get(typedefName);
   auto found = sema.LookupSingleName(sema.TUScope, identifier,
                                      clang::SourceLocation(),
                                      clang::Sema::LookupOrdinaryName);
   auto typedefDecl = dyn_cast_or_null<clang::TypedefDecl>(found);
   if (!typedefDecl)
      return {};

   auto underlyingTy =
      context.getCanonicalType(typedefDecl->getUnderlyingType());

   if (underlyingTy->getAs<clang::BuiltinType>())
      return underlyingTy;
   return {};
}

clang::CanQualType
ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM,
                                              CanStructType type) {
   // Handle builtin types by adding entries to the cache that reverse
   // the mapping done by the importer.  We could try to look at the
   // members of the struct instead, but even if that's ABI-equivalent
   // (which it had better be!), it might erase interesting semantic
   // differences like integers vs. characters.  This is important
   // because CC lowering isn't the only purpose of this conversion.
   //
   // The importer maps builtin types like 'int' to named types like
   // 'CInt', which are generally typealiases.  So what we do here is
   // map the underlying types of those typealiases back to the builtin
   // type.  These typealiases frequently create a many-to-one mapping,
   // so just use the first type that mapped to a particular underlying
   // type.
   //
   // This is the last thing that happens before asserting that the
   // struct type doesn't have a mapping.  Furthermore, all of the
   // builtin types are pre-built in the clang ASTContext.  So it's not
   // really a significant performance problem to just cache all them
   // right here; it makes making a few more entries in the cache than
   // we really need, but it also means we won't end up repeating these
   // stdlib lookups multiple times, and we have to perform multiple
   // lookups anyway because the MAP_BUILTIN_TYPE database uses
   // typealias names (like 'CInt') that aren't obviously associated
   // with the underlying C library type.

   auto stdlib = IGM.Context.getStdlibModule();
   assert(stdlib && "translating stdlib type to C without stdlib module?");
   auto &ctx = IGM.getClangAstContext();
   auto cacheStdlibType = [&](StringRef polarphpName,
                              clang::BuiltinType::Kind builtinKind) {
      CanType swiftType = getNamedPolarphpType(stdlib, polarphpName);
      if (!swiftType) return;

      auto &sema = IGM.Context.getClangModuleLoader()->getClangSema();
      // Handle Int and UInt specially. On Apple platforms, these correspond to
      // the NSInteger and NSUInteger typedefs, so map them back to those typedefs
      // if they're available, to ensure we get consistent ObjC @encode strings.
      if (swiftType->getAnyNominal() == IGM.Context.getIntDecl()) {
         if (auto NSIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSInteger")){
            Cache.insert({swiftType, NSIntegerTy});
            return;
         }
      } else if (swiftType->getAnyNominal() == IGM.Context.getUIntDecl()) {
         if (auto NSUIntegerTy =
            getClangBuiltinTypeFromTypedef(sema, "NSUInteger")) {
            Cache.insert({swiftType, NSUIntegerTy});
            return;
         }
      }

      Cache.insert({swiftType, getClangBuiltinTypeFromKind(ctx, builtinKind)});
   };

#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, POLAR_TYPE_NAME)          \
  cacheStdlibType(#POLAR_TYPE_NAME, clang::BuiltinType::CLANG_BUILTIN_KIND);
#include "polarphp/clangimporter/BuiltinMappedTypesDef.h"

   // On 64-bit Windows, no C type is imported as an Int or UInt; CLong is
   // imported as an Int32 and CLongLong as an Int64. Therefore, manually
   // add mappings to C for Int and UInt.
   // On 64-bit Cygwin, no manual mapping is required.
   if (IGM.Triple.isOSWindows() && !IGM.Triple.isWindowsCygwinEnvironment() &&
       IGM.Triple.isArch64Bit()) {
      // Map UInt to uintptr_t
      auto swiftUIntType = getNamedPolarphpType(stdlib, "UInt");
      auto clangUIntPtrType = ctx.getCanonicalType(ctx.getUIntPtrType());
      Cache.insert({swiftUIntType, clangUIntPtrType});

      // Map Int to intptr_t
      auto swiftIntType = getNamedPolarphpType(stdlib, "Int");
      auto clangIntPtrType = ctx.getCanonicalType(ctx.getIntPtrType());
      Cache.insert({swiftIntType, clangIntPtrType});
   }

   // The above code sets up a bunch of mappings in the cache; just
   // assume that we hit one of them.
   auto it = Cache.find(type);
   assert(it != Cache.end() &&
          "cannot translate Swift type to C! type is not specially known");
   return it->second;
}

clang::CanQualType GenClangType::visitTupleType(CanTupleType type) {
   unsigned e = type->getNumElements();
   if (e == 0)
      return getClangAstContext().VoidTy;

   CanType eltTy = type.getElementType(0);
   for (unsigned i = 1; i < e; i++) {
      assert(eltTy == type.getElementType(i) &&
             "Only tuples where all element types are equal "
             "map to fixed-size arrays");
   }

   auto clangEltTy = Converter.convert(IGM, eltTy);
   if (!clangEltTy) return clang::CanQualType();

   APInt size(32, e);
   auto &ctx = getClangAstContext();
   return ctx.getCanonicalType(
      ctx.getConstantArrayType(clangEltTy, size, nullptr,
                               clang::ArrayType::Normal, 0));
}

clang::CanQualType GenClangType::visitInterfaceType(CanInterfaceType type) {
//   auto proto = type->getDecl();
//   auto &clangCtx = getClangAstContext();
//
////   if (!proto->isObjC()) {
//// @todo
//   std::string s;
//   llvm::raw_string_ostream err(s);
//   err << "Trying to compute the clang type for a non-ObjC protocol type\n";
//   proto->dump(err);
//   llvm::report_fatal_error(err.str());
////   }
//
//   // Single protocol -> id<Proto>
//   clang::IdentifierInfo *name = &clangCtx.Idents.get(proto->getName().get());
//   auto *PDecl = clang::ObjCInterfaceDecl::Create(
//      const_cast<clang::ASTContext &>(clangCtx),
//      clangCtx.getTranslationUnitDecl(), name,
//      clang::SourceLocation(), nullptr);
//
//   // Attach an objc_runtime_name attribute with the Objective-C name to use
//   // for this protocol.
//   SmallString<64> runtimeNameBuffer;
//   PDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit(
//      PDecl->getASTContext(),
//      proto->getObjCRuntimeName(runtimeNameBuffer)));
//
//   auto clangType  = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy,
//                                                &PDecl, 1);
//   auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
//   return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) {
   return getClangMetatypeType(getClangAstContext());
}

clang::CanQualType
GenClangType::visitExistentialMetatypeType(CanExistentialMetatypeType type) {
   return getClangMetatypeType(getClangAstContext());
}

clang::CanQualType GenClangType::visitClassType(CanClassType type) {
   auto &clangCtx = getClangAstContext();
   auto polarphpDecl = type->getDecl();

   // TODO: [non-objc-class-clang-type-conversion]
   // Crashing here instead of returning a bogus 'id' leads to test failures,
   // which is surprising.
//   if (!polarphpDecl->isObjC())
// @todo
   return getClangIdType(clangCtx);

   // produce the clang type INTF * if it is imported ObjC object.
//   clang::IdentifierInfo *ForwardClassId =
//      &clangCtx.Idents.get(polarphpDecl->getName().get());
//   auto *CDecl = clang::ObjCInterfaceDecl::Create(
//      clangCtx, clangCtx.getTranslationUnitDecl(),
//      clang::SourceLocation(), ForwardClassId,
//      /*typeParamList*/nullptr, /*PrevDecl=*/nullptr,
//      clang::SourceLocation());
//
//   // Attach an objc_runtime_name attribute with the Objective-C name to use
//   // for this class.
//   SmallString<64> runtimeNameBuffer;
//   CDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit(
//      CDecl->getASTContext(),
//      polarphpDecl->getObjCRuntimeName(runtimeNameBuffer)));
//
//   auto clangType  = clangCtx.getObjCInterfaceType(CDecl);
//   auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
//   return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitBoundGenericClassType(
   CanBoundGenericClassType type) {
   // Any @objc class type in Swift that shows up in an @objc method maps 1-1 to
   // "id <SomeProto>"; with clang's encoding ignoring the protocol list.
   return getClangIdType(getClangAstContext());
}

clang::CanQualType
GenClangType::visitBoundGenericType(CanBoundGenericType type) {
   // We only expect *Pointer<T>, SIMD*<T> and Optional<T>.
   if (auto underlyingTy =
      PILType::getPrimitiveObjectType(type).getOptionalObjectType()) {
      // The underlying type could be a bridged type, which makes any
      // sort of casual assertion here difficult.
      return Converter.convert(IGM, underlyingTy.getAstType());
   }

   auto swiftStructDecl = type->getDecl();

   enum class StructKind {
      Invalid,
      UnsafeMutablePointer,
      UnsafePointer,
      AutoreleasingUnsafeMutablePointer,
      Unmanaged,
      CFunctionPointer,
      SIMD,
   } kind = llvm::StringSwitch<StructKind>(swiftStructDecl->getName().str())
      .Case("UnsafeMutablePointer", StructKind::UnsafeMutablePointer)
      .Case("UnsafePointer", StructKind::UnsafePointer)
      .Case(
         "AutoreleasingUnsafeMutablePointer",
         StructKind::AutoreleasingUnsafeMutablePointer)
      .Case("Unmanaged", StructKind::Unmanaged)
      .Case("CFunctionPointer", StructKind::CFunctionPointer)
      .StartsWith("SIMD", StructKind::SIMD)
      .Default(StructKind::Invalid);

   auto args = type.getGenericArgs();
   assert(args.size() == 1 &&
          "should have a single generic argument!");
   auto loweredArgTy = IGM.getLoweredType(args[0]).getAstType();

   switch (kind) {
      case StructKind::Invalid:
         llvm_unreachable("Unexpected non-pointer generic struct type in imported"
                          " Clang module!");

      case StructKind::UnsafeMutablePointer:
      case StructKind::Unmanaged:
      case StructKind::AutoreleasingUnsafeMutablePointer: {
         auto clangCanTy = Converter.convert(IGM, loweredArgTy);
         if (!clangCanTy) return clang::CanQualType();
         return getClangAstContext().getPointerType(clangCanTy);
      }
      case StructKind::UnsafePointer: {
         clang::QualType clangTy
            = Converter.convert(IGM, loweredArgTy).withConst();
         return getCanonicalType(getClangAstContext().getPointerType(clangTy));
      }

      case StructKind::CFunctionPointer: {
         auto &clangCtx = getClangAstContext();

         clang::QualType functionTy;
         if (isa<PILFunctionType>(loweredArgTy)) {
            functionTy = Converter.convert(IGM, loweredArgTy);
         } else {
            // Fall back to void().
            functionTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
         }
         auto fnPtrTy = clangCtx.getPointerType(functionTy);
         return getCanonicalType(fnPtrTy);
      }

      case StructKind::SIMD: {
         clang::QualType scalarTy = Converter.convert(IGM, loweredArgTy);
         auto numEltsString = swiftStructDecl->getName().str();
         numEltsString.consume_front("SIMD");
         unsigned numElts;
         bool failedParse = numEltsString.getAsInteger<unsigned>(10, numElts);
         assert(!failedParse && "SIMD type name didn't end in count?");
         (void) failedParse;
         auto vectorTy = getClangAstContext().getVectorType(scalarTy, numElts,
                                                            clang::VectorType::VectorKind::GenericVector);
         return getCanonicalType(vectorTy);
      }
   }

   llvm_unreachable("Not a valid StructKind.");
}

clang::CanQualType GenClangType::visitEnumType(CanEnumType type) {
   // Special case: Uninhabited enums are not @objc, so we don't
   // know what to do below, but we can just convert to 'void'.
   if (type->isUninhabited())
      return Converter.convert(IGM, IGM.Context.TheEmptyTupleType);
   // @todo
//   assert(type->getDecl()->isObjC() && "not an @objc enum?!");
//
//   // @objc enums lower to their raw types.
//   return Converter.convert(IGM,
//                            type->getDecl()->getRawType()->getCanonicalType());
}

clang::CanQualType GenClangType::visitFunctionType(CanFunctionType type) {
   llvm_unreachable("FunctionType should have been lowered away");
}

clang::CanQualType GenClangType::visitPILFunctionType(CanPILFunctionType type) {
   auto &clangCtx = getClangAstContext();

   enum FunctionPointerKind {
      Block, CFunctionPointer,
   };

   FunctionPointerKind kind;

   switch (type->getRepresentation()) {
      case PILFunctionType::Representation::Block:
         kind = Block;
         break;

      case PILFunctionType::Representation::CFunctionPointer:
         kind = CFunctionPointer;
         break;

      case PILFunctionType::Representation::Thick:
      case PILFunctionType::Representation::Thin:
      case PILFunctionType::Representation::Method:
      case PILFunctionType::Representation::ObjCMethod:
      case PILFunctionType::Representation::WitnessMethod:
      case PILFunctionType::Representation::Closure:
         llvm_unreachable("not an ObjC-compatible function");
   }

   // Convert the return and parameter types.
   auto allResults = type->getResults();
   assert(allResults.size() <= 1 && "multiple results with C convention");
   clang::QualType resultType;
   if (allResults.empty()) {
      resultType = clangCtx.VoidTy;
   } else {
      resultType = Converter.convert(IGM,
                                     allResults[0].getReturnValueType(IGM.getPILModule(), type));
      if (resultType.isNull())
         return clang::CanQualType();
   }

   SmallVector<clang::QualType, 4> paramTypes;
   SmallVector<clang::FunctionProtoType::ExtParameterInfo, 4> extParamInfos;
   for (auto paramTy : type->getParameters()) {
      clang::FunctionProtoType::ExtParameterInfo extParamInfo;

      // Blocks should only take direct +0 parameters.
      switch (paramTy.getConvention()) {
         case ParameterConvention::Direct_Guaranteed:
         case ParameterConvention::Direct_Unowned:
            // OK.
            break;

         case ParameterConvention::Direct_Owned:
            extParamInfo = extParamInfo.withIsConsumed(true);
            break;

         case ParameterConvention::Indirect_In:
         case ParameterConvention::Indirect_In_Constant:
         case ParameterConvention::Indirect_Inout:
         case ParameterConvention::Indirect_InoutAliasable:
         case ParameterConvention::Indirect_In_Guaranteed:
            llvm_unreachable("block takes indirect parameter");
      }
      auto param = Converter.convert(IGM,
                                     paramTy.getArgumentType(IGM.getPILModule(), type));
      if (param.isNull())
         return clang::CanQualType();

      paramTypes.push_back(param);
      extParamInfos.push_back(extParamInfo);
   }

   // Build the Clang function type.
   clang::FunctionProtoType::ExtProtoInfo extProtoInfo;
   extProtoInfo.ExtParameterInfos = extParamInfos.begin();

   auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, extProtoInfo);
   clang::QualType ptrTy;

   switch (kind) {
      case Block:
         ptrTy = clangCtx.getBlockPointerType(fnTy);
         break;
      case CFunctionPointer:
         ptrTy = clangCtx.getPointerType(fnTy);
   }
   return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitPILBlockStorageType(CanPILBlockStorageType type) {
   // We'll select (void)(^)(). This isn't correct for all blocks, but block
   // storage type should only be converted for function signature lowering,
   // where the parameter types do not matter.
   auto &clangCtx = getClangAstContext();
   auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
   auto blockTy = clangCtx.getBlockPointerType(fnTy);
   return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitInterfaceCompositionType(
   CanInterfaceCompositionType type) {
   auto &clangCtx = getClangAstContext();

   // FIXME. Eventually, this will have its own helper routine.
   SmallVector<const clang::ObjCProtocolDecl *, 4> Interfaces;
   auto layout = type.getExistentialLayout();
   assert(layout.isObjC() && "Cannot represent opaque existential in Clang");

   // AnyObject -> id.
   if (layout.isAnyObject())
      return getClangIdType(getClangAstContext());

   auto superclassTy = clangCtx.ObjCBuiltinIdTy;
   if (auto layoutSuperclassTy = layout.getSuperclass()) {
      superclassTy = clangCtx.getCanonicalType(
         cast<clang::ObjCObjectPointerType>(
            Converter.convert(IGM, CanType(layoutSuperclassTy)))
            ->getPointeeType());
   }

   for (Type t : layout.getInterfaces()) {
      auto opt = cast<clang::ObjCObjectPointerType>(
         Converter.convert(IGM, CanType(t)));
      for (auto p : opt->quals())
         Interfaces.push_back(p);
   }

   if (Interfaces.empty())
      return superclassTy;

   // id<protocol-list>
   clang::ObjCProtocolDecl **ProtoQuals =
      new(clangCtx) clang::ObjCProtocolDecl*[Interfaces.size()];
   memcpy(ProtoQuals, Interfaces.data(),
          sizeof(clang::ObjCInterfaceDecl*)*Interfaces.size());
   auto clangType = clangCtx.getObjCObjectType(superclassTy,
                                               ProtoQuals,
                                               Interfaces.size());
   auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
   return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitBuiltinRawPointerType(
   CanBuiltinRawPointerType type) {
   return getClangAstContext().VoidPtrTy;
}

clang::CanQualType GenClangType::visitBuiltinIntegerType(
   CanBuiltinIntegerType type) {
   auto &ctx = getClangAstContext();
   if (type->getWidth().isPointerWidth())
      return ctx.getCanonicalType(ctx.getUIntPtrType());
   assert(type->getWidth().isFixedWidth());
   auto width = type->getWidth().getFixedWidth();
   if (width == 1)
      return ctx.BoolTy;
   return ctx.getCanonicalType(ctx.getIntTypeForBitwidth(width, /*signed*/ 0));
}

clang::CanQualType GenClangType::visitBuiltinFloatType(
   CanBuiltinFloatType type) {
   auto &ctx = getClangAstContext();
   auto &clangTargetInfo = ctx.getTargetInfo();
   const llvm::fltSemantics *format = &type->getAPFloatSemantics();
   if (format == &clangTargetInfo.getHalfFormat()) return ctx.HalfTy;
   if (format == &clangTargetInfo.getFloatFormat()) return ctx.FloatTy;
   if (format == &clangTargetInfo.getDoubleFormat()) return ctx.DoubleTy;
   if (format == &clangTargetInfo.getLongDoubleFormat()) return ctx.LongDoubleTy;
   llvm_unreachable("cannot translate floating-point format to C");
}

clang::CanQualType GenClangType::visitArchetypeType(CanArchetypeType type) {
   // We see these in the case where we invoke an @objc function
   // through a protocol.
   return getClangIdType(getClangAstContext());
}

clang::CanQualType GenClangType::visitDynamicSelfType(CanDynamicSelfType type) {
   // Dynamic Self is equivalent to 'instancetype', which is treated as
   // 'id' within the Objective-C type system.
   return getClangIdType(getClangAstContext());
}

clang::CanQualType GenClangType::visitGenericTypeParamType(
   CanGenericTypeParamType type) {
   // We see these in the case where we invoke an @objc function
   // through a protocol argument that is a generic type.
   return getClangIdType(getClangAstContext());
}

clang::CanQualType GenClangType::visitType(CanType type) {
   llvm_unreachable("Unexpected type in Clang type generation.");
}

clang::CanQualType ClangTypeConverter::convert(IRGenModule &IGM, CanType type) {
   // Look in the cache.
   auto it = Cache.find(type);
   if (it != Cache.end()) {
      return it->second;
   }

   // Try to do this without making cache entries for obvious cases.
   if (auto nominal = dyn_cast<NominalType>(type)) {
      auto decl = nominal->getDecl();
      if (auto clangDecl = decl->getClangDecl()) {
         auto &ctx = IGM.getClangAstContext();
         if (auto clangTypeDecl = dyn_cast<clang::TypeDecl>(clangDecl)) {
            return ctx.getCanonicalType(ctx.getTypeDeclType(clangTypeDecl))
               .getUnqualifiedType();
         } else if (auto ifaceDecl = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) {
            auto clangType  = ctx.getObjCInterfaceType(ifaceDecl);
            auto ptrTy = ctx.getObjCObjectPointerType(clangType);
            return ctx.getCanonicalType(ptrTy);
         } else if (auto protoDecl = dyn_cast<clang::ObjCProtocolDecl>(clangDecl)){
            auto clangType  = ctx.getObjCObjectType(
               ctx.ObjCBuiltinIdTy,
               const_cast<clang::ObjCProtocolDecl **>(&protoDecl),
               1);
            auto ptrTy = ctx.getObjCObjectPointerType(clangType);
            return ctx.getCanonicalType(ptrTy);
         }
      }
   }

   // If that failed, convert the type, cache, and return.
   clang::CanQualType result = GenClangType(IGM, *this).visit(type);
   Cache.insert({type, result});
   return result;
}

clang::CanQualType IRGenModule::getClangType(CanType type) {
   return ClangTypes->convert(*this, type);
}

clang::CanQualType IRGenModule::getClangType(PILType type) {
   return getClangType(type.getAstType());
}

clang::CanQualType IRGenModule::getClangType(PILParameterInfo params,
                                             CanPILFunctionType funcTy) {
   auto paramTy = params.getPILStorageType(getPILModule(), funcTy);
   auto clangType = getClangType(paramTy);
   // @block_storage types must be @inout_aliasable and have
   // special lowering
   if (!paramTy.is<PILBlockStorageType>()) {
      if (params.isIndirectMutating()) {
         return getClangAstContext().getPointerType(clangType);
      }
      if (params.isFormalIndirect()) {
         auto constTy =
            getClangAstContext().getCanonicalType(clangType.withConst());
         return getClangAstContext().getPointerType(constTy);
      }
   }
   return clangType;
}

void IRGenModule::initClangTypeConverter() {
   if (auto loader = Context.getClangModuleLoader()) {
      auto importer = static_cast<ClangImporter*>(loader);
      ClangAstContext = &importer->getClangAstContext();
      ClangTypes = new ClangTypeConverter();
   } else {
      ClangAstContext = nullptr;
      ClangTypes = nullptr;
   }
}

void IRGenModule::destroyClangTypeConverter() {
   delete ClangTypes;
}
